<?php

namespace app\api\service;

use api\oilcard\Wikeyun;
use app\model\Agent;
use app\model\AgentServiceConfig;
use app\model\BaseSupplier;
use app\model\ChannelServDiscount;
use app\model\OilCardOrder;
use app\model\OilCardUploadQueue;
use app\model\OrderWkyOilCard;
use app\model\UserOilCard;
use think\facade\Db;

/*
油卡业务
当前支持单个供应商，如果是多供应商共存 =》
1，需要在用户油卡绑定的时候额外设置参数
2，api根据 请求参数的 supplier_id 获取 而不是根据 别名
3，
*/
class OilCardService extends CommonService
{
    public $supplier;
    public $api;
    private $supplierAlias = 'wky_oil_card'; // 供应商别名
    public $amountList = [500, 1000, 2000]; // 充值面额 单位 元
    public $orderModel = OilCardOrder::class;
    public $servType = PRODUCT_OILCARD_SERV;

    /*
     初始化service
    */
    public function __construct($iniSupplier = 1, $supplierAlias = '')
    {
        // 初始化 供应商  ==  预下单的时候不会初始化供应商，而是根据条件选择供应商时候调用 对 类的supplierAlias supplier api 赋值
        if ($iniSupplier) {
            $this->initSupplier($supplierAlias);
        }
    }

    /*
     初始化 供应商api 和供应商配置
    */
    public function initSupplier($supplierAlias = '')
    {
        if ($supplierAlias) {
            $this->supplierAlias = $supplierAlias;
        }
        $this->getSupplier(); // 获取供应商配置
        // 目前接入微客云 Wikeyun，不同供应商 引入不同 api
        $this->api = invoke(Wikeyun::class, [$this->supplier['api_config']]);
    }

    /*
     获取充值列表列表
     cnid 渠道id channel_id 简写
    */
    public function getGoods($cnid)
    {
        $result = [];
        // 获取渠道的充值配置
        $oilCardDiscount = ChannelServDiscount::where('channel_id', $cnid)->where('serv_type', $this->servType)->select();
        if ($oilCardDiscount->isEmpty()) {
            // 后台没有 配置的话
            foreach ($this->amountList as $k => $v) {
                $temp = [
                    'recharge_type' => 1,
                    'name' => '慢充',
                    'discount' => 10,
                    'origin_amount' => $v,
                    'amount' => $v,
                ];
                $result[0][$v] = $temp;
            }
        } else {
            foreach ($this->amountList as $k => $v) {
                foreach ($oilCardDiscount as $k2 => $v2) {
                    $temp = [
                        'recharge_type' => $v2->sub_type, // $this->getRechangeTypeMap($v2->sub_type)
                        'name' => 1 == $v2->sub_type ? '慢充' : '快充',
                        'discount' => $v2->value,
                        'origin_amount' => $v,
                        'amount' => round($v2->value * $v / 100, 2),
                    ];
                    $result[$temp['recharge_type']][$v] = $temp;
                }
            }
        }

        return $result;
    }

    /*
     我们平台快慢充和第三方平台的映射关系
    */
    public function getRechangeTypeMap($ourType)
    {
        $mapArr = [
            'wky_oil_card' => [  // key是我们平台定义快慢充的key  0是 第三方平台的定义
                '1' => 0,
                '2' => 1,
            ],
        ];

        return  $mapArr[$this->supplierAlias][$ourType] ?? -1;
    }

    /*
     获取供应商配置
    */
    public function getSupplier()
    {
        return $this->supplier = BaseSupplier::withJoin(['rechargeConfig' => function ($query) {
            $query->withField(['supplier_id', 'fast_state', 'fast_settle_value', 'fast_priority', 'slow_state', 'slow_settle_value', 'slow_priority']);
        }])->where('code', $this->supplierAlias)->find();
    }

    /*
     添加用户加油卡
     todo 确认同个油卡卡号多次向 供应商发送添加 或者编辑成其他卡号 是怎么样的返回
     param [card_num=>'卡号',name=>'姓名',phone=>'手机号',user_num=>'身份证号',card_type=>'油卡类型']
     userId 用户id
     existsValite 存在时候返回成功或失败 。 批量充值时候卡号存在的直接返回成功
    */
    public function addOilCard($param, $userId, $existsValite = 0)
    {
        // 检查存在性
        $cardInfo = UserOilCard::where('card_num', $param['card_num'])->where('user_id', $userId)->find();
        if ($cardInfo) {
            if ($existsValite) {
                return $this->valiReturn($cardInfo);
            }

            return $this->inValiReturn('您已经添加过这张卡了');
        }

        $res = $this->api->addCard($param);
        if ('0000' == $res['code']) {
            // 入库
            $param['user_id'] = $userId;
            $param['card_id'] = $res['data']['id'];
            $cardInfo = new UserOilCard();
            if (!$cardInfo->save($param)) {
                return $this->inValiReturn('油卡添加失败，请重试或者联系我们');
            }

            return $this->valiReturn($cardInfo);
        } else {
            return $this->inValiReturn($res['msg']);
        }
    }

    /*
     供应商处的油卡信息
    */
    public function infoOilCard($id)
    {
        // 检查存在性
        $cardInfo = UserOilCard::find($id);
        $res = $this->api->cardInfo(['card_id' => $cardInfo->card_id]);
        if ('0000' == $res['code']) {
            return $this->valiReturn($res);
        } else {
            return $this->inValiReturn($res['msg']);
        }
    }

    /*
     修改用户加油卡
     param [card_num=>'卡号',name=>'姓名',phone=>'手机号',user_num=>'身份证号',card_type=>'油卡类型']
     userId 用户id
    */
    public function editOilCard($id, $param, $userId)
    {
        // 检查存在性
        $cardInfo = UserOilCard::where('user_id', $userId)->find($id);
        if (!$cardInfo) {
            return $this->inValiReturn('你的卡已经不存在');
        }

        $param['card_id'] = $cardInfo->card_id;
        $res = $this->api->editCard($param);
        if ('0000' == $res['code']) {
            // 入库
            foreach ($param as $k => $v) {
                $cardInfo->{$k} = $v;
            }
            if (!$cardInfo->save($param)) {
                return $this->inValiReturn('油卡信息修改失败，请重试或者联系我们');
            }
        } else {
            return $this->inValiReturn($res['msg']);
        }

        return $this->valiReturn($cardInfo);
    }

    /*  删除用户油卡 */
    public function deleteOilCard($id, $userId)
    {
        // 检查存在性
        $cardInfo = UserOilCard::withTrashed()->where('user_id', $userId)->find($id);
        if (!$cardInfo) {
            return $this->inValiReturn('你的卡已经不存在');
        }
        UserOilCard::destroy($id);
        $res = $this->api->delCard($cardInfo->toArray());
        if ('0000' == $res['code']) {
            return $this->valiReturn();
        } else {
            return $this->inValiReturn($res['msg']);
        }
    }

    /*
     代理商后台批量上传 充值excel文件处理, 完成，用户油卡信息 ，订单入库 以及 推送第三方
    */
    public function adminBatchPay($data = null)
    {
        // step todo 根据油卡类型 以及 快慢充优先规则 选择供应商
        $this->initSupplier();

        $data = OilCardUploadQueue::whereNull('queued_at')->where('card_id', '')->select();
        if (!$data->isEmpty()) {
            // 添加 油卡
            try {
                foreach ($data as $k => $v) {
                    $res = $this->addOilCard($v->toArray(), 0, 1);
                    if ($res['code']) {
                        doWarning(getExceptionMsg($res['msg'])); // 单个失败 继续下一个
                        continue;
                    }
                    $data[$k]->card_id = $res['data']['card_id'];
                    $data[$k]->save();
                }
            } catch (\Exception $e) {
                doWarning(getExceptionMsg($e));

                return $this->inValiReturn('批量充值失败，请重试或者联系我们');
            }
        }

        // 重新获取队列
        $data = OilCardUploadQueue::whereNull('queued_at')->where('card_id', '<>', '')->select();

        // 处理订单
        $orderService = invoke(OrderService::class, [$this->servType]);
        $agentList = [];
        $agentServiceList = [];
        Db::startTrans();
        try {
            foreach ($data as $k => $v) {
                if (!isset($agentList[$v->agent_id])) {
                    $agentList[$v->agent_id] = Agent::find($v->agent_id);
                }
                if (!isset($agentServiceList[$v->agent_id])) {
                    $agentServiceList[$v->agent_id] = AgentServiceConfig::where('agent_id', $v->agent_id)
                                                        ->where('serv_type', $this->servType)
                                                        ->where('status', 1)
                                                        ->find();
                }
                if (!isset($agentServiceList[$v->agent_id])) {
                    // 业务关闭 此时做警报
                    continue;
                }

                $orderGoods = [
                    'recharge_type' => $v->recharge_type,
                    'card_type' => $v->card_type,
                    'card_id' => $v->card_id,
                    'card_user_name' => $v->name,
                    'card_num' => $v->card_num,
                    'store_id' => $this->supplier->api_config['store_id'],
                ];
                $priceInfo = ['origin_amount' => $v->origin_amount, 'amount' => 0];
                $res = $orderService->adminPay($priceInfo, $this->supplier, $agentList[$v->agent_id], $orderGoods, $v->recharge_type, $agentServiceList[$v->agent_id], $k);
                if ($res['code']) {
                    Db::rollback();

                    return $res;
                }

                // step 代理商余额处理
                $orderInfo = $res['data']['order'];
                $res = $orderService->successAgentBalance($orderInfo);
                if ($res['code']) {
                    Db::rollback();

                    return $res;
                }
                // step 处理供应商处的余额
                $res = $orderService->successSupplierBalance($orderInfo);
                if ($res['code']) {
                    Db::rollback();

                    return $res;
                }

                // 推送供应商
                $param = [
                    'store_id' => $this->supplier->api_config['store_id'],
                    'cardId' => $orderGoods['card_id'],
                    'order_no' => $orderInfo['sn'],
                    'amount' => fen2yuan($v->origin_amount),
                    'recharge_type' => $orderGoods['recharge_type'],
                ];

                $this->pushOrder($param, $orderInfo['id']);

                // 更新队列
                $v->queued_at = date('Y-m-d H:i:s');
                $v->save();
            }

            Db::commit();

            return $this->valiReturn();
        } catch (\Exception $e) {
            doWarning(getExceptionMsg($e));
            Db::rollback();

            return $this->inValiReturn('批量');
        }
    }

    /*
     暂时 废弃，分块事务
    */
    public function adminBatchPayChunkTrans($data = null)
    {
        // step todo 根据油卡类型 以及 快慢充优先规则 选择供应商
        $this->initSupplier();

        $data = OilCardUploadQueue::whereNull('queued_at')->where('card_id', '')->select();
        if (!$data->isEmpty()) {
            // 添加 油卡
            try {
                foreach ($data as $k => $v) {
                    $res = $this->addOilCard($v->toArray(), 0, 1);
                    if ($res['code']) {
                        doWarning(getExceptionMsg($res['msg'])); // 单个失败 继续下一个
                        continue;
                    }
                    $data[$k]->card_id = $res['data']['card_id'];
                    $data[$k]->save();
                }
            } catch (\Exception $e) {
                doWarning(getExceptionMsg($e));

                return $this->inValiReturn('批量充值失败，请重试或者联系我们');
            }
        }

        // 重新获取队列
        $data = OilCardUploadQueue::whereNull('queued_at')->where('card_id', '<>', '')->select();
        // 处理订单
        $orderService = invoke(OrderService::class, [$this->servType]);
        $agentList = [];
        $agentServiceList = [];
        try {
            foreach ($data as $k => $v) {
                // 每10条开启 一次事务已经提示事务
                if ($k / 10) {
                    if (isset($newTrans) && !$newTrans) {
                        Db::commit();
                    }
                    $newTrans = 1;
                } else {
                    $newTrans = 0;
                }

                if ($newTrans) {
                    Db::startTrans();
                }

                if (!isset($agentServiceList[$v->agent_id])) {
                    $agentServiceList[$v->agent_id] = AgentServiceConfig::where('agent_id', $v->agent_id)
                                                        ->where('serv_type', $this->servType)
                                                        ->where('status', 1)
                                                        ->find();
                }
                if (!isset($agentServiceList[$v->agent_id])) {
                    // 业务关闭 此时做警报
                    continue;
                }
                $orderGoods = [
                    'recharge_type' => $v->recharge_type,
                    'card_type' => $v->card_type,
                    'card_id' => $v->card_id,
                    'card_user_name' => $v->name,
                    'card_num' => $v->card_num,
                    'store_id' => $this->supplier->api_config['store_id'],
                ];
                $priceInfo = ['origin_amount' => $v->origin_amount, 'amount' => 0];
                $res = $orderService->adminPay($priceInfo, $this->supplier, $agentList[$v->agent_id], $orderGoods, $v->recharge_type, $agentServiceList[$v->agent_id], $k);
                if ($res['code']) {
                    if (!$newTrans) {
                        Db::rollback();
                        continue;
                    }
                }

                // step 代理商余额处理
                $orderInfo = $res['data']['order'];
                $res = $orderService->successAgentBalance($orderInfo);
                if ($res['code']) {
                    if (!$newTrans) {
                        Db::rollback();
                        continue;
                    }
                }
                // step 处理供应商处的余额
                $res = $orderService->successSupplierBalance($orderInfo);
                if ($res['code']) {
                    if (!$newTrans) {
                        Db::rollback();
                        continue;
                    }
                }

                // 推送供应商
                $param = [
                    'store_id' => $this->supplier->api_config['store_id'],
                    'cardId' => $orderGoods['card_id'],
                    'order_no' => $orderInfo['sn'],
                    'amount' => fen2yuan($v->origin_amount),
                    'recharge_type' => $orderGoods['recharge_type'],
                ];

                $this->pushOrder($param, $orderInfo['id']);

                // 更新队列
                $v->queued_at = date('Y-m-d H:i:s');
                $v->save();
            }

            // 最后一条提交事务
            if (!isset($data[$k + 1])) {
                Db::commit();
            }

            return $this->valiReturn();
        } catch (\Exception $e) {
            doWarning(getExceptionMsg($e));

            return $this->inValiReturn('批量');
        }
    }

    /*
    充值预支付
    */
    public function prepay($param, $userInfo)
    {
        //step 获取用户油卡
        $oilCard = UserOilCard::find($param['oil_card_id']);
        if (!$oilCard) {
            return $this->inValiReturn('油卡不存在，请刷新或者联系我们');
        }

        // step todo 根据油卡类型 以及 快慢充优先规则 选择供应商
        $this->initSupplier();

        // step 获取商品 并做价格校验
        $goodsList = $this->getGoods($param['cnid']);
        if (empty($goodsList[$param['recharge_type']])) {
            return $this->inValiReturn('充值类型，请刷新页面重试或者联系我们');
        }
        if (empty($goodsList[$param['recharge_type']][$param['origin_amount']])) {
            return $this->inValiReturn('充值面额不存在，请刷新页面重试或者联系我们');
        }
        $goodsInfo = $this->getGoods($param['cnid'])[$param['recharge_type']][$param['origin_amount']];
        if ($param['amount'] !== $goodsInfo['amount']) {
            return $this->invaliReturn('价格校验失败，当前价格已经变动了请刷新页面重试或者联系我们');
        }
        // 把商品转化成数据库存储的格式
        $orderGoods = [];
        if ('wky_oil_card' == $this->supplierAlias) { // 不同供应商对应的商品字段不一样
            $orderGoods['store_id'] = $this->supplier->api_config['store_id'];
            $orderGoods['card_user_name'] = $oilCard->name;
            $orderGoods['card_num'] = $oilCard->card_num;
            $orderGoods['card_id'] = $oilCard->card_id;
            $orderGoods['card_type'] = $oilCard->card_type;
            $orderGoods['user_name'] = $userInfo->name;
            $orderGoods['recharge_type'] = $param['recharge_type'];
        }
        unset($goodsInfo);
        // 预支付
        return invoke(OrderService::class, [$this->servType])->prepay($this->supplier, $param['cnid'], $orderGoods, $userInfo, $param['pay_type']);
    }

    /*
    支付完成后回调
     sn 订单号
     data 回调相关参数

     1，更新订单状态
     2，扣除代理商余额
     3，发送第三方
    */
    public function paySuccess($sn, $data)
    {
        $res = invoke(OrderService::class, [$this->servType])->orderSuccess($sn, $data);
        if ($res['code']) {
            return $this->inValiReturn($res['msg']);
        }

        $orderGoods = OrderWkyOilCard::where('order_id', $res['data']['order']['id'])->find();
        if (!$orderGoods) {
            return $this->inValiReturn('我们出错了，充值信息不存在，请联系我们');
        }
        // 通知第三方
        $param = [
            'store_id' => $orderGoods['store_id'],
            'cardId' => $orderGoods['card_id'],
            'order_no' => $res['data']['order']['sn'],
            'amount' => fen2yuan($orderGoods['amount']),
            'recharge_type' => $orderGoods['recharge_type'],
        ];

        return $this->pushOrder($param, $res['data']['order']['id']);
    }

    /*
     充值回调
    */
    public function supplierNotify($data)
    {
        // step 签名校验
        if ($this->api->getSign($data) !== $data['sign']) {
            return $this->inValiReturn('签名错误');
        }

        $param = [
            'sn' => $data['order_no'],
            'supplier_order_sn' => $data['order_number'],
        ];

        return invoke(SupplierService::class)->setOrderModel($this->orderModel)->notifyTrans($param, $this->statusToOrder($data['status']));
    }

    /*
     支付成功后通知第三方
     $param  具体看第三方文档
     微客云 [store_id,cardId,order_no,amount,recharge_type],
    */
    public function pushOrder($param, $orderId)
    {
        if ('local' == config('app.app_env')) { // 本地环境直接返回不作实际发送
            OilCardOrder::where('id', $orderId)->update(['status' => 1]);

            return $this->valiReturn();
        }

        $res = $this->api->create($param);
        if ('0000' == $res['code']) {
            // 更新提交状态
            OilCardOrder::where('id', $orderId)->update(['status' => 1, 'supplier_notice_at' => date('Y-m-d H:i:s')]);

            return $this->valiReturn();
        } else {
            OilCardOrder::where('id', $orderId)->update(['status' => 2, 'supplier_notice_at' => date('Y-m-d H:i:s')]);

            return $this->inValiReturn($res['msg']);
        }
    }

    /* 状态转订单order 取值 */
    public function statusToOrder($status = '')
    {
        // 更新 交易结果（0 待支付 1 已付 充值中 2充 值成功 3充值失败 需要退款 4退款成功 6 待充值 7 已匹配）
        // `status` tinyint(3) DEFAULT '0' COMMENT '0未提交 1已提交  2提交失败 3充值成功  4充值失败 5订单处理中',
        $statusArr = [
            1 => 5,
            2 => 3,
            3 => 4,
            4 => 4,
            6 => 1,
            7 => 1,
        ];

        if ($status) {
            return $statusArr[$status];
        }

        return $statusArr;
    }
}
